﻿using System;
using SoftwareConsulting.BI11.NumericalMethods.Interfaces;

namespace SoftwareConsulting.BI11.NumericalMethods.Statistics
{    
    /// <summary>
    /// Класс вычисления интеграла многоаргументной функции с использованием приближения методом Монте-Карло
    /// </summary>
    public class ManyVariableMonteCarloIntegrator
    {	    
        private IManyVariableFunction _function;//интегрируемая функция	    
        private double[] _a;//нижняя граница интегрирования	    
        private double[] _range;//диапазон интегрирования

	    /// <summary>
	    /// Конструктор класса
	    /// </summary>
        /// <param name="function">интегрируемая функция</param>
        /// <param name="lowLimit">нижняя граница интегрирования</param>
        /// <param name="highLimit">верхняя граница интегрирования</param>
        public ManyVariableMonteCarloIntegrator(IManyVariableFunction function, double[] lowLimit, double[] highLimit)
        {
            _function = function;
            _a = lowLimit.Clone() as double[];
            int n = lowLimit.Length;
            _range = new double[n];
            for (int i = 0; i < n; i++)
            {
                _range[i] = highLimit[i] - lowLimit[i];
            }
        }
        
        /// <summary>
        /// Возвращает решение
        /// </summary>
        /// <param name="iterationsCount">число итераций</param>
        /// <param name="upperLimit">значение большее, чем максимум функции в области интегрирования</param>
        /// <returns></returns>
        public double GetSolution(int iterationsCount, double upperLimit)
        {
            int n = _a.Length;
            double[] x = new double[n];
            int hits = 0;
            Random random = new Random();
            for (int count = 0; count < iterationsCount; count++)
            {
                for (int i = 0; i < n; i++)
                    x[i] = _a[i] + random.NextDouble() * _range[i];
                if (random.NextDouble() * upperLimit <= _function.GetValue(x))
                    hits++;
            }
            double result = upperLimit * (hits / (double)iterationsCount);
            for (int i = 0; i < n; i++)
                result *= _range[i];
            return result;
        }
    }
}
